import { BaseResponse } from '@/common/BaseResponse';
import { IntegralTask } from '@/entity/integral-task.entity';
import { InvitationCode } from '@/entity/invitation-code.entity';
import { StudyMatchNeed } from '@/entity/study-match-need.entity';
import { StudyMatchResult } from '@/entity/study-match-result.entity';
import { StudyMatchUserMessge } from '@/entity/study-match-user-message.entity';
import { User } from '@/entity/user.entity';
import { FINISH_STATUS, RECORD_STATUS } from '@/enum/common';
import { HTTP_CUSTOM_CODE } from '@/enum/http-custom-code';
import { REDIS_NAME_SPACE } from '@/enum/redis-namespace';
import { STUDY_MATCH_STATUS } from '@/enum/study-match';
import { getRandomCode } from '@/utils/code';
import { RedisService } from '@chenjm/nestjs-redis';
import { HttpException, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import Redis, { RedisKey } from 'ioredis';
import { Repository } from 'typeorm';
import { InvitationCodeDto } from './dto/invitation-code.dto';
import { StudyMatchNeedDto } from './dto/study-match-need.dto';
import { UserMessageDto } from './dto/user-message.dto';

@Injectable()
export class StudyMatchService {
  private readonly redis: Redis;

  constructor(
    @InjectRepository(StudyMatchUserMessge)
    private readonly studyMatchUserMessage: Repository<StudyMatchUserMessge>,
    @InjectRepository(StudyMatchNeed)
    private readonly studyMatchNeedRepository: Repository<StudyMatchNeed>,
    @InjectRepository(StudyMatchResult)
    private readonly studyMatchResultRepository: Repository<StudyMatchResult>,
    @InjectRepository(InvitationCode)
    private readonly invitationCodeRepository: Repository<InvitationCode>,
    private readonly redisService: RedisService,
    @InjectRepository(User)
    private readonly userRepository: Repository<User>,
    @InjectRepository(IntegralTask)
    private readonly integralTaskRepository: Repository<IntegralTask>,
  ) {}

  setRedis(key: RedisKey, value: string, seconds?: number) {
    const redisKey = REDIS_NAME_SPACE.INVITATION_CODE + key;
    if (!seconds) {
      return this.redis.set(redisKey, value);
    } else {
      return this.redis.set(redisKey, value, 'EX', seconds);
    }
  }

  getRedis(key: RedisKey) {
    return this.redis.get(REDIS_NAME_SPACE.INVITATION_CODE + key);
  }

  async setInvitaionCode(invitationCodeDto: InvitationCodeDto) {
    // 设置邀请码10分钟
    this.setRedis(
      String(invitationCodeDto.code),
      JSON.stringify(invitationCodeDto),
      60 * 10,
    );
  }

  async submitUserMessage(userMessage: UserMessageDto) {
    // 先查询是否已经存入信息
    const result = await this.studyMatchUserMessage.findOne({
      where: {
        userId: userMessage.userId,
      },
    });
    if (!result) {
      const studyMatchUserMessage = new StudyMatchUserMessge();
      Object.assign(studyMatchUserMessage, userMessage);
      return this.studyMatchUserMessage.save(studyMatchUserMessage);
    }
    Object.assign(result, userMessage);
    return this.studyMatchUserMessage.update(result.id, result);
  }

  getUserMessage(id: number) {
    return this.studyMatchUserMessage.findOne({
      where: {
        userId: id,
      },
    });
  }

  async submitStudyMatchNeed(studyMatchNeedDto: StudyMatchNeedDto) {
    // 先查询是否已经存入信息
    const result = await this.studyMatchNeedRepository.findOne({
      where: {
        userId: studyMatchNeedDto.userId,
      },
    });
    if (!result) {
      const studyMatchNeed = new StudyMatchNeed();
      Object.assign(studyMatchNeed, studyMatchNeedDto);
      studyMatchNeed.status = RECORD_STATUS.SHOW;
      return this.studyMatchNeedRepository.save(studyMatchNeed);
    }
    Object.assign(result, studyMatchNeedDto);
    result.status = RECORD_STATUS.SHOW;
    return this.studyMatchNeedRepository.update(result.id, result);
  }

  getStudyMatchNeed(id: number) {
    return this.studyMatchNeedRepository.findOne({
      where: {
        userId: id,
      },
    });
  }

  async cancelStudyMatch(id: number) {
    const result = await this.studyMatchNeedRepository.findOne({
      where: {
        userId: id,
      },
    });
    if (!result) {
      throw new HttpException('暂未填写匹配信息', 500);
    }
    result.status = RECORD_STATUS.HIDDEN;
    return this.studyMatchNeedRepository.update(result.id, result);
  }

  async getStudyMatchStatus(id: number) {
    const macthResult = await this.studyMatchResultRepository.findOne({
      where: [
        {
          matchOne: id,
        },
        {
          matchTwo: id,
        },
      ],
    });

    if (macthResult) {
      macthResult.matchOneUserInfo = await this.userRepository.findOne({
        where: {
          id: macthResult.matchOne,
        },
      });
      macthResult.matchTwoUserInfo = await this.userRepository.findOne({
        where: {
          id: macthResult.matchTwo,
        },
      });
      return BaseResponse.toSuccesJustData({
        code: STUDY_MATCH_STATUS.MATCHED,
        ...macthResult,
      });
    }
    const result = await this.studyMatchNeedRepository.findOne({
      where: {
        userId: id,
      },
    });
    if (!result) {
      return BaseResponse.toSuccesJustData({
        code: STUDY_MATCH_STATUS.NOT_MATCH,
      });
    }
    if (result.status === RECORD_STATUS.SHOW) {
      return BaseResponse.toSuccesJustData({
        code: STUDY_MATCH_STATUS.MATCHING,
      });
    }
    return BaseResponse.toSuccesJustData({
      code: STUDY_MATCH_STATUS.NOT_MATCH,
    });
  }

  async createInvitationCode(userId: number) {
    const result = await this.invitationCodeRepository.findOne({
      where: {
        userId: userId,
        status: RECORD_STATUS.SHOW,
      },
    });

    if (result) {
      result.code = getRandomCode();
      result.status = RECORD_STATUS.SHOW;
      await this.invitationCodeRepository.update(result.id, result);
      return result;
    }

    const invitationCode = new InvitationCode();
    invitationCode.code = getRandomCode();
    invitationCode.userId = userId;
    invitationCode.status = RECORD_STATUS.SHOW;
    return this.invitationCodeRepository.save(invitationCode);
  }

  async matchUserByInvitationCode(invitationCodeDto: InvitationCodeDto) {
    const result = await this.invitationCodeRepository.findOne({
      where: {
        code: invitationCodeDto.code,
      },
    });
    if (result && result.userId === invitationCodeDto.userId) {
      throw new HttpException(
        '不能匹配您自己哦',
        HTTP_CUSTOM_CODE.CANNOT_MATCH_SELFT,
      );
    }

    if (!result) {
      throw new HttpException(
        '不存在该邀请码',
        HTTP_CUSTOM_CODE.NOT_INCLUDE_CODE,
      );
    }

    // 您是否已被匹配
    const matchResultMessage = await this.studyMatchResultRepository.findOne({
      where: [
        {
          matchOne: invitationCodeDto.userId,
        },
        {
          matchTwo: invitationCodeDto.userId,
        },
      ],
    });

    if (matchResultMessage) {
      throw new HttpException('您已有匹配伴侣，不能重复匹配哦', 500);
    }

    const matchResult = new StudyMatchResult();
    matchResult.matchOne = result.userId;
    matchResult.matchTwo = invitationCodeDto.userId;
    return this.studyMatchResultRepository.save(matchResult);
  }

  async addStudyMatchScore(id: number, num: number, type: number) {
    // 您是否已被匹配
    const matchResultMessage = await this.studyMatchResultRepository.findOne({
      where: [
        {
          matchOne: id,
        },
        {
          matchTwo: id,
        },
      ],
    });

    if (!matchResultMessage) {
      throw new HttpException('请先去匹配', 500);
    }

    // 判断今日是否完成任务
    const qb = this.integralTaskRepository.createQueryBuilder('integralTask');
    const result = await qb
      .andWhere('match_id = :matchId and task_type = :type', {
        matchId: matchResultMessage.id,
        type: type,
      })
      .andWhere('to_days(create_time) = to_days(now())')
      .getOne();
    console.log(JSON.stringify(result));

    if (result) {
      throw new HttpException('今日任务已完成', 500);
    }
    const integralTask = new IntegralTask();
    integralTask.matchId = matchResultMessage.id;
    integralTask.taskType = type;
    integralTask.integral = num;
    integralTask.status = FINISH_STATUS.FINISHED;
    await this.integralTaskRepository.save(integralTask);

    matchResultMessage.integral += num;
    return this.studyMatchResultRepository.update(
      matchResultMessage.id,
      matchResultMessage,
    );
  }

  async getTaskList(matchId: number) {
    const qb = this.integralTaskRepository.createQueryBuilder('integralTask');
    const result = await qb
      .andWhere('match_id = :matchId', {
        matchId: matchId,
      })
      .andWhere('to_days(create_time) = to_days(now())')
      .getMany();
    return result;
  }
}
